import { dirs, exists, files, readFile } from './util'
import { JavaFile, parseJavaFile } from './java/java-file'
import { ResourceFile } from './java/resource-file'
import { rootPath, serve } from './env'
import * as path from 'path'
import getValue from 'get-value'

export const templateRootPath = serve ? path.join(rootPath, 'templates') : path.join(rootPath, 'resources/templates')

export class TemplateConfig {
    readonly placeholder: string
    readonly javaDir: string
    readonly resourcesDir: string
    readonly buildFile: string | string[]

    constructor(props) {
        this.placeholder = getValue(props, 'placeholder')
        this.javaDir = getValue(props, 'directory.java')
        this.resourcesDir = getValue(props, 'directory.resources')
        this.buildFile = getValue(props, 'buildFile')
    }
}

async function loadJava(absPath: string) {
    const ls = await files(absPath, true)
    const ret: string[] = []
    for (const d of ls) {
        if (d.endsWith('.java')) {
            ret.push(d)
        }
    }
    return ret
}

async function loadResources(absPath: string) {
    return await files(absPath, true)
}

async function getResourceAndBuildFiles(config: TemplateConfig, paths: string[]) {
    if (!config) {
        return []
    }
    const ret: ResourceFile[] = []
    for (const p of paths) {
        const pathObj = path.parse(p)
        const content = await readFile(p)
        const resourceFile = new ResourceFile({
            filename: pathObj.base,
            path: p,
            content,
        })
        ret.push(resourceFile)
    }
    return ret
}

export class Template {
    readonly name: string
    readonly parent: string
    private javaFiles: string[] = []
    private resourcesFiles: string[] = []
    private buildFiles: string[] = []
    private initial = false
    // tslint:disable-next-line:variable-name
    private _config?: TemplateConfig

    get config() {
        // tslint:disable-next-line:no-non-null-assertion
        return this._config!!
    }

    constructor(name: string, dir: string) {
        this.name = name
        this.parent = dir
    }

    public async load() {
        if (!this.initial) {
            const config = await readFile(path.resolve(this.parent, this.name, 'template.json'))
            this._config = new TemplateConfig(JSON.parse(config))

            if (this._config) {
                const absJavaDir = path.resolve(this.parent, this.name, this._config.javaDir)
                this.javaFiles = await loadJava(absJavaDir)

                const absResourcesDir = path.resolve(this.parent, this.name, this._config.resourcesDir)
                this.resourcesFiles = await loadResources(absResourcesDir)

                if (this._config.buildFile) {
                    const buildFiles = Array.isArray(this._config.buildFile) ? this._config.buildFile : [this._config.buildFile]
                    for (let i = 0; i < buildFiles.length; i++) {
                        buildFiles[i] = path.resolve(this.parent, this.name, buildFiles[i])
                    }
                    this.buildFiles = buildFiles
                }
            }

            this.initial = true
        }
    }

    public async getJavaFiles() {
        await this.load()
        if (!this._config) {
            return []
        }
        const ret: JavaFile[] = []
        for (const f of this.javaFiles) {
            const pathObj = path.parse(f)
            const content = await readFile(f)
            const info = parseJavaFile(content)
            const javaFile = new JavaFile({
                filename: pathObj.base,
                path: f,
                package: info.package,
                className: info.className,
                content,
            })
            ret.push(javaFile)
        }
        return ret
    }

    public async getResourceFiles() {
        await this.load()
        // tslint:disable-next-line:no-non-null-assertion
        return await getResourceAndBuildFiles(this._config!!, this.resourcesFiles)
    }

    public async getBuildFiles() {
        await this.load()
        // tslint:disable-next-line:no-non-null-assertion
        return await getResourceAndBuildFiles(this._config!!, this.buildFiles)
    }
}

export async function listTemplates() {
    const ret: Template[] = []
    const ls = await dirs(templateRootPath)
    for (const t of ls) {
        if (await exists(path.resolve(t, 'template.json'))) {
            const p = path.parse(t)
            ret.push(new Template(p.name, p.dir))
        }
    }
    return ret;
}

async function test() {
    const temps = await listTemplates();
    await temps[0].getJavaFiles()
}

// test()
